package DataModel;

import java.io.File;
import java.util.ArrayList;

import Interfaces.Track;
import SortSearch.BinarySearch;

/**
 * This class represents a list containing up to several {@link Locus}. A
 * {@link Locus} contains a second list with the {@link FeatureBucket} where
 * {@link Feature} are placed in. The class also contains some search functions
 * for {@link Feature} Information.
 * 
 * INFO: GeneScapes uses only the first element in the loci list. But this class
 * was implemented to load and multi genbank file and allows to choose out of
 * different loci.
 * 
 * @author Stefan Lorenz <br>
 *         <b>Date:</b> 12.11.2012
 */
public class LociHolder implements Track {

	/**
	 * The list contains all loci
	 */
	private ArrayList<Locus> lociList;

	/**
	 * Name of the file where the data is from
	 */
	private String name;

	/**
	 * File where the data is from
	 */
	private File file;

	/**
	 * The binary search for searching for features by positions
	 */

	private BinarySearch search;
	/**
	 * The file format
	 */
	private String typeOfFile;

	/**
	 * Contrsuctor of an empty {@link LociHolder}
	 */
	public LociHolder() {
		this.name = "";
		this.file = new File("");
		this.lociList = new ArrayList<Locus>();
		this.search = new BinarySearch();
		this.typeOfFile = "";
	}

	/**
	 * Sets a list of loci
	 * 
	 * @param lociList
	 *            list of loci
	 */
	public void setListOfLoci(ArrayList<Locus> lociList) {
		this.lociList = lociList;
	}

	/**
	 * Returns a list of loci
	 * 
	 * @return list of loci
	 */
	public ArrayList<Locus> getListOfLoci() {
		return lociList;
	}

	/**
	 * Sets the name of the file where data is parsed from
	 * 
	 * @param name
	 *            name of the file
	 */
	private void setName() {
		this.name = this.file.getName();
		;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Interfaces.Track#getName()
	 */
	public String getName() {
		return this.name;
	}

	/**
	 * Sets the file of the data
	 * 
	 * @param file
	 *            file from data
	 */
	public void setFile(File file) {
		this.file = file;
		this.setName();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Interfaces.Track#getFile()
	 */
	public File getFile() {
		return this.file;
	}

	/**
	 * Returns the number of {@link Feature}s of all loci
	 * 
	 * @return the number of all features in all loci
	 */
	public int getNumberOfAllFeatures() {
		int number = 0;
		for (int i = 0; i < this.getListOfLoci().size(); i++) {
			for (int j = 0; j < this.getListOfLoci().get(i).getBucketList().size(); j++) {
				number += this.getListOfLoci().get(i).getBucketList().get(j).getFeaturesOfBucket().size();
			}
		}
		return number;
	}

	/**
	 * Returns the number of {@link Feature}s of a {@link Locus} defined by
	 * name. If locus name isn't found 0 will return
	 * 
	 * 
	 * @param locusName
	 *            name of the locus
	 * @return number of all features on a defined locus
	 */
	public int getNumberOfFeatures(String locusName) {
		for (int i = 0; i < this.getListOfLoci().size(); i++) {
			if (locusName.equals(this.getListOfLoci().get(i).getLocusName()))
				return this.getListOfLoci().get(i).getLocusSize();
		}
		return 0;
	}

	/**
	 * Returns the genome sequence of a {@link Locus}. If the file doesn't
	 * contain a sequence null gets returned
	 * 
	 * @param locusName
	 *            the name of the locus
	 * @return the sequence of the locus
	 */
	public String getSequence(String locusName) {
		for (int i = 0; i < this.getListOfLoci().size(); i++) {
			if (locusName.equals(this.getListOfLoci().get(i).getLocusName()))
				return this.getListOfLoci().get(i).getSequence().toString();
		}
		return null;
	}

	/**
	 * Returns the sequence of the first {@link Locus} in the lociList
	 * 
	 * @return the sequence of the locus
	 */
	public String getSequence() {
		return this.getListOfLoci().get(0).getSequence().toString();
	}

	/**
	 * Returns a region of the sequence of a locus. The positions are zero based
	 * 
	 * @param locusName
	 *            name of the locus
	 * @param start
	 *            start position of the sequence
	 * @param end
	 *            last position of the sequence
	 * @return a part of the sequence given by the locus name and the position
	 *         or <code>null</code>
	 */
	public String getSequenceFromTo(String locusName, int start, int end) {
		for (int i = 0; i < this.getListOfLoci().size(); i++) {
			if (locusName.equals(this.getListOfLoci().get(i).getLocusName())) {
				try {
					return this.getListOfLoci().get(i).getSequence().substring(start - 1, end).toString();
				} catch (StringIndexOutOfBoundsException e) {
					e.fillInStackTrace();
				}

			}
		}
		return null;
	}

	/**
	 * Returns several regions of the sequence of a locus.
	 * 
	 * @param locusName
	 *            name of the locus
	 * @param positionList
	 *            list of {@link Tupel}s which contain the start and stop
	 *            positions
	 * @return an array with the single sequences
	 */
	public ArrayList<String> getSplicedSequenceFromTo(String locusName, ArrayList<Tupel> positionList) {
		ArrayList<String> seq = new ArrayList<String>();

		for (int i = 0; i < this.getListOfLoci().size(); i++) {
			if (locusName.equals(this.getListOfLoci().get(i).getLocusName())) {
				for (int j = 0; j < positionList.size(); j++) {
					seq.add(getSequenceFromTo(locusName, positionList.get(j).getStart(), positionList.get(j).getStop()));
				}
			}
		}
		return seq;
	}

	/**
	 * Returns the index of the features next to the start position on the given
	 * locus
	 * 
	 * @param locusName
	 *            name of the locus
	 * @param start
	 *            start position
	 * @param stop
	 *            stop position
	 * @return the index of the feature next to the start position
	 */
	public int getFeatureInRange(String locusName, int start, int stop) {
		// Searching for the locus index
		int index = 0;
		for (int i = 0; i < this.lociList.size(); i++) {
			if (this.lociList.get(i).getLocusName().equals(locusName)) {
				index = i;
				break;
			}
		}
		// searchign for feature index
		int featureIndex = this.search.binarySearchTranscriptionalActiveRegion(this.lociList.get(index).getBucketList(), start);
		if (featureIndex == 0)
			return featureIndex;
		// test if some features have the same positions between start and stop
		// and go to the most left one
		while (this.lociList.get(index).getBucketList().get(featureIndex).getStop() > start) {
			featureIndex--;
		}
		return featureIndex;
	}

	/**
	 * Returns the index of the bucket
	 * 
	 * @param s
	 *            name of the bucket
	 * @return index of the bucket
	 */
	public int getBucketByName(String s) {
		for (int i = 0; i < this.getListOfLoci().get(0).getBucketList().size(); i++) {
			if (this.getListOfLoci().get(0).getBucketList().get(i).getName().equals(s))
				return i;
		}
		return -1;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Interfaces.Track#getTypeOfFile()
	 */
	public String getTypeOfFile() {
		return this.typeOfFile;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Interfaces.Track#getElementsOfTrack()
	 */
	public int getElementsOfTrack() {
		int number = 0;
		for (int i = 0; i < this.lociList.get(0).getBucketList().size(); i++) {
			number += this.lociList.get(0).getBucketList().get(i).getFeaturesOfBucket().size();
		}
		return number;
	}

	/**
	 * Returns the bucket list of the first locus in the list
	 * 
	 * @return the list of buckets
	 */
	public ArrayList<FeatureBucket> getList() {
		return this.lociList.get(0).getBucketList();
	}

	/**
	 * Sets bucketlist of the first locus
	 * 
	 * @param bucketList
	 *            list of buckets
	 */
	public void setList(ArrayList<FeatureBucket> bucketList) {
		this.lociList.get(0).setFeatureBucket(bucketList);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Interfaces.Track#getSize()
	 */
	@Override
	public int getSize() {
		if (this.lociList.get(0).getSequence().length() != 0)
			return this.lociList.get(0).getSequence().length();
		else {
			return this.lociList.get(0).getBucketList().get(this.lociList.get(0).getBucketList().size() - 1).getStop();
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Interfaces.Track#setTrackIndexToFeatures(int)
	 */
	@Override
	public void setTrackIndexToFeatures(int index) {
		for (int i = 0; i < this.lociList.size(); i++) {
			for (int j = 0; j < this.lociList.get(i).getBucketList().size(); j++) {
				for (int j2 = 0; j2 < this.lociList.get(i).getBucketList().get(j).getFeaturesOfBucket().size(); j2++) {
					this.lociList.get(i).getBucketList().get(j).getFeaturesOfBucket().get(j2).setTrackIndex(index);
				}
			}
		}
	}

	/**
	 * Sets the type of the file
	 * 
	 * @param typeOfFile
	 *            the fiel format
	 */
	public void setTypeOfFile(String typeOfFile) {
		this.typeOfFile = typeOfFile;
	}
}
